package com.ipan.kits.base;

import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Map;

import com.google.common.base.Objects;
import com.ipan.kits.base.annotation.Nullable;

/**
 * 1. Object打印优化，主要解决数组的打印
 * 
 * 2. 多个对象的HashCode串联
 */
public class ObjectUtil {

	private static final String NULL = "null";

	/**
	 * JDK7 引入的Null安全的equals
	 */
	public static boolean equals(@Nullable Object a, @Nullable Object b) {
		return Objects.equal(a, b);
	}

	/**
	 * 多个对象的HashCode串联, 组成新的HashCode
	 */
	public static int hashCode(Object... objects) {
		return Arrays.hashCode(objects);
	}

	/**
	 * 对象的toString(), 处理了对象为数组的情况，JDK的默认toString()只打数组的地址如
	 * "[Ljava.lang.Integer;@490d6c15.
	 */
	public static String toPrettyString(Object value) {
		if (value == null) {
			return NULL;
		}

		Class<?> type = value.getClass();

		if (type.isArray()) {
			Class componentType = type.getComponentType();

			if (componentType.isPrimitive()) {
				return primitiveArrayToString(value, componentType);
			} else {
				return objectArrayToString(value);
			}
		} else if (value instanceof Iterable) {
			// 因为Collection的处理也是默认调用元素的toString(),
			// 为了处理元素是数组的情况，同样需要重载
			return collectionToString(value);
		}

		return value.toString();
	}

	private static String primitiveArrayToString(Object value, Class componentType) {
		StringBuilder sb = new StringBuilder();

		if (componentType == int.class) {
			sb.append(Arrays.toString((int[]) value));
		} else if (componentType == long.class) {
			sb.append(Arrays.toString((long[]) value));
		} else if (componentType == double.class) {
			sb.append(Arrays.toString((double[]) value));
		} else if (componentType == float.class) {
			sb.append(Arrays.toString((float[]) value));
		} else if (componentType == boolean.class) {
			sb.append(Arrays.toString((boolean[]) value));
		} else if (componentType == short.class) {
			sb.append(Arrays.toString((short[]) value));
		} else if (componentType == byte.class) {
			sb.append(Arrays.toString((byte[]) value));
		} else if (componentType == char.class) {
			sb.append(Arrays.toString((char[]) value));
		} else {
			throw new IllegalArgumentException("unsupport array type");
		}

		return sb.toString();
	}

	private static String objectArrayToString(Object value) {
		StringBuilder sb = new StringBuilder();
		sb.append('[');

		Object[] array = (Object[]) value;
		for (int i = 0; i < array.length; i++) {
			if (i > 0) {
				sb.append(", ");
			}
			sb.append(toPrettyString(array[i]));
		}
		sb.append(']');
		return sb.toString();
	}

	private static String collectionToString(Object value) {
		Iterable iterable = (Iterable) value;
		StringBuilder sb = new StringBuilder();
		sb.append('{');
		int i = 0;
		for (Object o : iterable) {
			if (i > 0) {
				sb.append(',');
			}
			sb.append(toPrettyString(o));
			i++;
		}
		sb.append('}');
		return sb.toString();
	}

	/**
	 * 如果给定对象为{@code null}返回默认值
	 *
	 * <pre>
	 * ObjectUtil.defaultIfNull(null, null)      = null
	 * ObjectUtil.defaultIfNull(null, "")        = ""
	 * ObjectUtil.defaultIfNull(null, "zz")      = "zz"
	 * ObjectUtil.defaultIfNull("abc", *)        = "abc"
	 * ObjectUtil.defaultIfNull(Boolean.TRUE, *) = Boolean.TRUE
	 * </pre>
	 *
	 * @param <T>
	 *            对象类型
	 * @param object
	 *            被检查对象，可能为{@code null}
	 * @param defaultValue
	 *            被检查对象为{@code null}返回的默认值，可以为{@code null}
	 * @return 被检查对象为{@code null}返回默认值，否则返回原值
	 * @since 3.0.7
	 */
	public static <T> T defaultIfNull(final T object, final T defaultValue) {
		return (null != object) ? object : defaultValue;
	}

	/**
	 * 对象比较，比较结果取决于comparator，如果被比较对象为null，传入的comparator对象应处理此情况<br>
	 * 如果传入comparator为null，则使用默认规则比较（此时被比较对象必须实现Comparable接口）
	 *
	 * <p>
	 * 一般而言，如果c1 &lt; c2，返回数小于0，c1==c2返回0，c1 &gt; c2 大于0
	 *
	 * @param <T>
	 *            被比较对象类型
	 * @param c1
	 *            对象1
	 * @param c2
	 *            对象2
	 * @param comparator
	 *            比较器
	 * @return 比较结果
	 * @see java.util.Comparator#compare(Object, Object)
	 * @since 4.6.9
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static <T> int compare(T c1, T c2, Comparator<T> comparator) {
		if (null == comparator) {
			return compare((Comparable) c1, (Comparable) c2);
		}
		return comparator.compare(c1, c2);
	}

	/**
	 * {@code null}安全的对象比较，{@code null}对象小于任何对象
	 *
	 * @param <T>
	 *            被比较对象类型
	 * @param c1
	 *            对象1，可以为{@code null}
	 * @param c2
	 *            对象2，可以为{@code null}
	 * @return 比较结果，如果c1 &lt; c2，返回数小于0，c1==c2返回0，c1 &gt; c2 大于0
	 * @see java.util.Comparator#compare(Object, Object)
	 */
	public static <T extends Comparable<? super T>> int compare(T c1, T c2) {
		return compare(c1, c2, false);
	}

	/**
	 * {@code null}安全的对象比较
	 *
	 * @param <T>
	 *            被比较对象类型（必须实现Comparable接口）
	 * @param c1
	 *            对象1，可以为{@code null}
	 * @param c2
	 *            对象2，可以为{@code null}
	 * @param isNullGreater
	 *            当被比较对象为null时是否排在后面，true表示null大于任何对象，false反之
	 * @return 比较结果，如果c1 &lt; c2，返回数小于0，c1==c2返回0，c1 &gt; c2 大于0
	 * @see java.util.Comparator#compare(Object, Object)
	 */
	public static <T extends Comparable<? super T>> int compare(T c1, T c2, boolean isNullGreater) {
		if (c1 == c2) {
			return 0;
		} else if (c1 == null) {
			return isNullGreater ? 1 : -1;
		} else if (c2 == null) {
			return isNullGreater ? -1 : 1;
		}
		return c1.compareTo(c2);
	}

	/**
	 * 自然比较两个对象的大小，比较规则如下：
	 *
	 * <pre>
	 * 1、如果实现Comparable调用compareTo比较
	 * 2、o1.equals(o2)返回0
	 * 3、比较hashCode值
	 * 4、比较toString值
	 * </pre>
	 *
	 * @param <T>
	 *            被比较对象类型
	 * @param o1
	 *            对象1
	 * @param o2
	 *            对象2
	 * @param isNullGreater
	 *            null值是否做为最大值
	 * @return 比较结果，如果o1 &lt; o2，返回数小于0，o1==o2返回0，o1 &gt; o2 大于0
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static <T> int compare(T o1, T o2, boolean isNullGreater) {
		if (o1 == o2) {
			return 0;
		} else if (null == o1) {// null 排在后面
			return isNullGreater ? 1 : -1;
		} else if (null == o2) {
			return isNullGreater ? -1 : 1;
		}

		if (o1 instanceof Comparable && o2 instanceof Comparable) {
			// 如果bean可比较，直接比较bean
			return ((Comparable) o1).compareTo(o2);
		}

		if (o1.equals(o2)) {
			return 0;
		}

		int result = Integer.compare(o1.hashCode(), o2.hashCode());
		if (0 == result) {
			result = compare(o1.toString(), o2.toString());
		}

		return result;
	}

	/**
	 * * 判断一个Collection是否为空， 包含List，Set，Queue
	 * 
	 * @param coll
	 *            要判断的Collection
	 * @return true：为空 false：非空
	 */
	public static boolean isEmpty(Collection<?> coll) {
		return isNull(coll) || coll.isEmpty();
	}

	/**
	 * * 判断一个Collection是否非空，包含List，Set，Queue
	 * 
	 * @param coll
	 *            要判断的Collection
	 * @return true：非空 false：空
	 */
	public static boolean isNotEmpty(Collection<?> coll) {
		return !isEmpty(coll);
	}

	/**
	 * * 判断一个对象数组是否为空
	 * 
	 * @param objects
	 *            要判断的对象数组
	 ** @return true：为空 false：非空
	 */
	public static boolean isEmpty(Object[] objects) {
		return isNull(objects) || (objects.length == 0);
	}

	/**
	 * * 判断一个对象数组是否非空
	 * 
	 * @param objects
	 *            要判断的对象数组
	 * @return true：非空 false：空
	 */
	public static boolean isNotEmpty(Object[] objects) {
		return !isEmpty(objects);
	}

	/**
	 * * 判断一个Map是否为空
	 * 
	 * @param map
	 *            要判断的Map
	 * @return true：为空 false：非空
	 */
	public static boolean isEmpty(Map<?, ?> map) {
		return isNull(map) || map.isEmpty();
	}

	/**
	 * * 判断一个Map是否为空
	 * 
	 * @param map
	 *            要判断的Map
	 * @return true：非空 false：空
	 */
	public static boolean isNotEmpty(Map<?, ?> map) {
		return !isEmpty(map);
	}

	/**
	 * * 判断一个对象是否为空
	 * 
	 * @param object
	 *            Object
	 * @return true：为空 false：非空
	 */
	public static boolean isNull(Object object) {
		return object == null;
	}

	/**
	 * * 判断一个对象是否非空
	 * 
	 * @param object
	 *            Object
	 * @return true：非空 false：空
	 */
	public static boolean isNotNull(Object object) {
		return !isNull(object);
	}

	/**
	 * * 判断一个对象是否是数组类型（Java基本型别的数组）
	 * 
	 * @param object
	 *            对象
	 * @return true：是数组 false：不是数组
	 */
	public static boolean isArray(Object object) {
		return isNotNull(object) && object.getClass().isArray();
	}
	
	@SuppressWarnings("unchecked")
    public static <T> T cast(Object obj) {
        return (T) obj;
    }

}
